package com.maoshi.shop.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.maoshi.shop.bean.dto.OrderRefundDto;
import com.maoshi.shop.bean.enums.*;
import com.maoshi.shop.bean.event.OrderRefundSuccessEvent;
import com.maoshi.shop.bean.event.ReceiptOrderEvent;
import com.maoshi.shop.bean.model.*;
import com.maoshi.shop.bean.param.OrderPayParam;
import com.maoshi.shop.bean.param.OrderRefundParam;
import com.maoshi.shop.bean.param.StatisticsRefundParam;
import com.maoshi.shop.bean.pay.RefundInfoDto;
import com.maoshi.shop.common.config.Constant;
import com.maoshi.shop.common.enums.PayType;
import com.maoshi.shop.common.exception.MaoshiShopBindException;
import com.maoshi.shop.common.util.Arith;
import com.maoshi.shop.dao.OrderMapper;
import com.maoshi.shop.dao.OrderRefundMapper;
import com.maoshi.shop.dao.ShopWalletMapper;
import com.maoshi.shop.service.*;
import lombok.AllArgsConstructor;
import ma.glasnost.orika.MapperFacade;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Service
@AllArgsConstructor
public class OrderRefundServiceImpl extends ServiceImpl<OrderRefundMapper, OrderRefund> implements OrderRefundService {

    private final OrderRefundMapper orderRefundMapper;
    private final OrderService orderService;
    private final ProductService productService;
    private final SkuService skuService;
    private final OrderItemService orderItemService;
    private final RefundOrderSettlementService refundOrderSettlementService;
    private final RefundDeliveryService refundDeliveryService;
    private final OrderSettlementService orderSettlementService;

    private final RefundAddrService refundAddrService;

    private final ShopWalletMapper shopWalletMapper;
    private final OrderMapper orderMapper;
    private final MapperFacade mapperFacade;
    private final ShopWalletLogService shopWalletLogService;

    private final ApplicationContext applicationContext;
    private final PayManagerService payManagerService;

    /**
     * 获取分页对象
     */
    @Override
    public IPage<OrderRefundDto> getPage(Page page, OrderRefundDto orderRefundDto, String startTime, String endTime) {
        IPage<OrderRefundDto> orderRefundDtoIPage = orderRefundMapper.getPage(page, orderRefundDto, startTime, endTime);
        List<OrderRefundDto> records = orderRefundDtoIPage.getRecords();
        for (OrderRefundDto record : records) {
            getRefundTypeByOrderItem(record);
        }
        return orderRefundDtoIPage;
    }

    /**
     * 根据编码查询退款单信息
     *
     * @param refundSn 退款单编码
     * @return 退款包装对象
     */
    @Override
    public OrderRefundDto getOrderRefundByRefundSn(String refundSn) {
        OrderRefundDto orderRefundDto = orderRefundMapper.getOrderRefundByRefundSn(refundSn);
        if (orderRefundDto == null) {
            return null;
        }
        //获取总数，如果为0则为全部
        if(orderRefundDto.getGoodsNum() == 0){
            Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNumber, orderRefundDto.getOrderNumber()));
            orderRefundDto.setGoodsNum(order.getProductNums());
        }
        // 判断退款单类型（1:整单退款,2:单个物品退款）
        getRefundTypeByOrderItem(orderRefundDto);
        return orderRefundDto;
    }

    /**
     * 根据退款ID获取退款单信息
     *
     * @param refundId 退款单ID
     * @return
     */
    @Override
    public OrderRefundDto getOrderRefundById(Long refundId, Long shopId) {
        OrderRefundDto orderRefundDto = orderRefundMapper.getById(refundId, shopId);
        if (orderRefundDto == null) {
            throw new MaoshiShopBindException("无法获取退款订单信息");
        }
        // 判断退款单类型（1:整单退款,2:单个物品退款）
        getRefundTypeByOrderItem(orderRefundDto);
        return orderRefundDto;
    }

    /**
     * 生成退款信息和比率信息
     *
     * @param shopId 商家id
     */
    @Override
    public StatisticsRefundParam getOrderRefundByShopId(Long shopId, Date startTime, Date endTime) {
        StatisticsRefundParam refundParam = orderRefundMapper.getRefundByShopId(shopId, startTime, endTime);
        OrderPayParam oderParam = orderService.getPayUserCountByshopId(shopId, startTime, endTime);
        StatisticsRefundParam yesterdayRefundParam = orderRefundMapper.getRefundByShopId(shopId, DateUtil.beginOfDay(DateUtil.yesterday()),
                DateUtil.endOfDay(DateUtil.yesterday()));
        OrderPayParam yesterdayOderParam = orderService.getPayUserCountByshopId(shopId, DateUtil.beginOfDay(DateUtil.yesterday()),
                DateUtil.endOfDay(DateUtil.yesterday()));
        StatisticsRefundParam lastWeekRefundParam = orderRefundMapper.getRefundByShopId(shopId, DateUtil.beginOfDay(DateUtil.lastWeek()),
                DateUtil.endOfDay(DateUtil.lastWeek()));
        OrderPayParam lastWeekOderParam = orderService.getPayUserCountByshopId(shopId, DateUtil.beginOfDay(DateUtil.lastWeek()),
                DateUtil.endOfDay(DateUtil.lastWeek()));
        double yesterdayRate = 0.00;
        double lastWeekRate = 0.00;
        double refundRate = 0.00;
        double yesterdayRefundRate = 0.00;
        double lastweekRefundRate = 0.00;
        //金额比率
        if (yesterdayRefundParam.getPayActualTotal() != 0) {
            yesterdayRate = Arith.div(Arith.mul(refundParam.getPayActualTotal(), 100), yesterdayRefundParam.getPayActualTotal(), 2);
        }
        if (lastWeekRefundParam.getPayActualTotal() != 0) {
            lastWeekRate = Arith.div(Arith.mul(refundParam.getPayActualTotal(), 100), lastWeekRefundParam.getPayActualTotal(), 2);
        }
        //退款比率
        if (oderParam.getPayOrderCount() != 0) {
            refundRate = Arith.div(Arith.mul(refundParam.getRefundCount(), 100), oderParam.getPayOrderCount(), 2);
        }
        if (yesterdayOderParam.getPayOrderCount() != 0) {
            yesterdayRefundRate = Arith.div(Arith.mul(refundParam.getRefundCount(), 100), yesterdayOderParam.getPayOrderCount(), 2);
        }
        if (lastWeekOderParam.getPayOrderCount() != 0) {
            lastweekRefundRate = Arith.div(Arith.mul(lastWeekRefundParam.getRefundCount(), 100), lastWeekOderParam.getPayOrderCount(), 2);
        }
        yesterdayRefundRate = refundRate - yesterdayRefundRate;
        lastweekRefundRate = refundRate - lastweekRefundRate;
        refundParam.setYesterdayAmountRate(yesterdayRate);
        refundParam.setSevenDayAmountRate(lastWeekRate);
        refundParam.setRefundRate(refundRate);
        refundParam.setYesterdayRefundRate(yesterdayRefundRate);
        refundParam.setSevenDayRefundRate(lastweekRefundRate);
        return refundParam;
    }


    /**
     * 生成退款信息和分段比率信息
     *
     * @param shopId 商家id
     */
    @Override
    public List<StatisticsRefundParam> getOrderRefundByShopIdAndDay(Long shopId, Date startTime, Date endTime) {
        List<StatisticsRefundParam> refundList = orderRefundMapper.getRefundByShopIdAndDay(shopId, startTime, endTime);
        List<OrderPayParam> orderList = orderMapper.getPayOrderByTime(shopId, startTime, endTime);
        List<StatisticsRefundParam> refundNewList = new ArrayList<>();
        for (int i = 0; i < refundList.size(); i++) {
            StatisticsRefundParam refundParam = refundList.get(i);
            if(i + 1 > orderList.size()){
                break;
            }
            OrderPayParam orderParam = orderList.get(i);
            Double rate = 0.00;
            if (orderParam.getPayOrderCount() != 0) {
                rate = Arith.div(Arith.mul(refundParam.getRefundCount(), 100), orderParam.getPayOrderCount(), 2);

            } else if (refundParam.getRefundCount() != 0) {
                rate = 100.00;
            }

            refundParam.setRefundRate(rate);
            refundList.set(i, refundParam);
            if (i % 7 == 0) {
                refundParam.setRefundDateToString(DateUtil.formatDate(refundParam.getRefundDate()));
                refundNewList.add(refundParam);
            }

        }
        return refundNewList;
    }

    /**
     * 根据商品名生成退款排行
     *
     * @param shopId 商家id
     */
    @Override
    public List<StatisticsRefundParam> getRefundRankingByProd(Long shopId, Date beginTime, Date endTime) {
        List<StatisticsRefundParam> ranking = orderRefundMapper.getRefundRankingByProd(shopId, beginTime, endTime);
        return ranking;
    }

    /**
     * 根据退款原因生成退款排行
     *
     * @param shopId 商家id
     */
    @Override
    public List<StatisticsRefundParam> getRefundRankingByReason(Long shopId, Date beginTime, Date endTime) {
        List<StatisticsRefundParam> ranking = orderRefundMapper.getRefundRankingByReason(shopId, beginTime, endTime);
        List<StatisticsRefundParam> rank = new ArrayList<>();
        double sum = 0.00;
        for (StatisticsRefundParam refundParam : ranking) {
            sum = Arith.add(sum, refundParam.getPayActualTotal());
        }
        for (StatisticsRefundParam refundParam : ranking) {
            refundParam.setRefundRate(Arith.div(Arith.mul(refundParam.getPayActualTotal(), 100), sum, 2));
            rank.add(refundParam);
        }
        return rank;
    }

    /**
     * 生成退款单
     *
     * @param orderRefund 退款对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyRefund(OrderRefund orderRefund) {
        // 生成退款单
        save(orderRefund);
        // 更新订单状态
        Order order = orderService.getById(orderRefund.getOrderId());
        order.setRefundStatus(RefundStatusEnum.APPLY.value());
        orderService.updateById(order);
    }

    /**
     * 判断退款单类型（1:整单退款,2:单个物品退款）
     */
    private void getRefundTypeByOrderItem(OrderRefundDto orderRefundDto) {
        if (Objects.equals(orderRefundDto.getRefundType(), RefundType.ALL.value())) {
            List<OrderItem> items = orderItemService.getOrderItemsByOrderNumber(orderRefundDto.getOrderNumber());
            orderRefundDto.setOrderItems(items);
        } else {
            OrderItem item = orderItemService.getById(orderRefundDto.getOrderItemId());
            orderRefundDto.getOrderItems().add(item);
        }
    }

    /**
     * 卖家处理（退款/返回退货地址）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderRefundDto processRefundOrder(OrderRefundParam orderRefundParam, Long shopId) {
        OrderRefundDto orderRefundDto = getOrderRefundById(orderRefundParam.getRefundId(), shopId);

        Date now = new Date();


        // 是否同意退款
        if (Objects.equals(orderRefundParam.getRefundSts(), RefundStsType.DISAGREE.value())) {
            // 不同意退款
            disagreeRefund(orderRefundParam, orderRefundDto);
        } else {
            // 正在处理退款的状态
            orderRefundDto.setReturnMoneySts(ReturnMoneyStsType.PROCESSING.value());

            // 判断是退款退货/仅退款(1:仅退款 2:退款退货)
            if (Objects.equals(orderRefundDto.getApplyType(), 1)) {
                // 同意退款
                agreeRefund(orderRefundDto);
                // 减少商家结算金额
                subShopSettlementAmount(orderRefundDto);
            } else {
                // 同意退款退货操作
                // 设置退货物流信息
                RefundAddr refundAddr = refundAddrService.getById(orderRefundParam.getRefundAddrId());
                if (refundAddr == null) {
                    throw new MaoshiShopBindException("请设置退货物流信息");
                }
                RefundDelivery refundDelivery = new RefundDelivery();
                refundDelivery.setShopId(orderRefundDto.getShopId());
                refundDelivery.setRefundSn(orderRefundDto.getRefundSn());
                refundDelivery.setUserId(orderRefundDto.getUserId());
                refundDelivery.setReceiverName(refundAddr.getReceiverName());
                refundDelivery.setReceiverMobile(refundAddr.getReceiverMobile());
                refundDelivery.setReceiverTelephone(refundAddr.getReceiverTelephone());
                refundDelivery.setReceiverPostCode(refundAddr.getPostCode());
                String addr = refundAddr.getProvince() + refundAddr.getCity() + refundAddr.getArea() + refundAddr.getAddr();
                refundDelivery.setReceiverAddr(addr);
                refundDelivery.setCreateTime(new Date());
                refundDeliveryService.save(refundDelivery);
            }
        }

        // 设置卖家备注
        orderRefundDto.setSellerMsg(orderRefundParam.getSellerMsg());
        // 受理时间
        orderRefundDto.setHandelTime(now);
        // 更新时间
        orderRefundDto.setUpdateTime(now);

        // 更新数据
        updateById(orderRefundDto);
        return orderRefundDto;
    }
    /**
     * 收到货同意退款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderRefundDto returnMoney(OrderRefundParam orderRefundParam, Long shopId) {
        // 获取退款单信息
        OrderRefundDto orderRefundDto = getOrderRefundById(orderRefundParam.getRefundId(), shopId);

        if (!Objects.equals(ReturnMoneyStsType.CONSIGNMENT.value(), orderRefundDto.getReturnMoneySts())) {
            throw new MaoshiShopBindException("订单状态已经发生改变，请勿重复操作");
        }

        Date now = new Date();

        // 是否同意退款
        if (Objects.equals(orderRefundParam.getRefundSts(), RefundStsType.AGREE.value())) {
            // 减少商家结算金额
            subShopSettlementAmount(orderRefundDto);

            // 同意操作
            orderRefundDto.setReturnMoneySts(ReturnMoneyStsType.RECEIVE.value());
            // 同意退款
            agreeRefund(orderRefundDto);

        } else {
            disagreeRefund(orderRefundParam, orderRefundDto);
        }

        // 是否收到货
        orderRefundDto.setIsReceiver(orderRefundParam.getIsReceiver());
        // 收货时间
        orderRefundDto.setReceiveTime(now);
        // 更新时间
        orderRefundDto.setUpdateTime(now);

        // 更新数据
        updateById(orderRefundDto);

        return orderRefundDto;
    }

    /**
     * 减少商家结算金额/未结算金额
     *
     */
    private void subShopSettlementAmount(OrderRefundDto orderRefundDto) {
        Long shopId = orderRefundDto.getShopId();
        // 同意退款，判断商家是否还有金额进行退款
        Order order = orderService.getOrderByOrderNumber(orderRefundDto.getOrderNumber());
        if (order == null) {
            throw new MaoshiShopBindException("未找到该订单信息");
        }
        double shopRealRefundAmount = orderRefundDto.getRefundAmount();

        // 订单发起退款时，扣除商家金额应该加上平台部分
        // 如果订单100元，平台出10元给商家（如平台10元优惠券，会员折扣9折），用户支付90元，此时商家结算时，要拿到100元，。
        // 当用户进行退款，有一部分钱是平台出的，平台出的这部分钱，要将平台出的这部分钱按数据库存的平台优惠金额从商家那里扣除，如用户退90块，商家结算减少100块。
        if(Objects.nonNull(order.getPlatformAmount())&& order.getPlatformAmount() > 0){
            if (Objects.equals(orderRefundDto.getRefundType(), RefundType.ALL.value())) {
                shopRealRefundAmount = Arith.add(shopRealRefundAmount,order.getPlatformAmount());
            } else {
                OrderItem item = orderItemService.getById(orderRefundDto.getOrderItemId());
                shopRealRefundAmount = Arith.add(shopRealRefundAmount,item.getPlatformShareReduce());
            }
        }



        // 店铺真正退款的金额是不用退分销员的那部分的
        // 如果订单100元，分销10元，10元的分销费用由商家出，此时商家结算时，拿到90元。
        // 当用户进行退款，退1块的时候，分销会无效，此时商家拿回给出去的10元分销费用，并将1元给到退款用户，此时商家因为退款赚9元。 shopRealRefundAmount = -9
        // 当用户进行退款，退11块的时候，分销会无效，此时商家拿回给出去的10元分销费用，并将11元给到退款用户，此时商家因为退款亏1元。shopRealRefundAmount = 1
        if (orderRefundDto.getDistributionTotalAmount() != null) {
            shopRealRefundAmount =  Arith.sub(shopRealRefundAmount, orderRefundDto.getDistributionTotalAmount());
        }

        ShopWallet shopWallet = shopWalletMapper.selectOne(new LambdaQueryWrapper<ShopWallet>().eq(ShopWallet::getShopId, shopId));
        ShopWallet newShopWallet = mapperFacade.map(shopWallet, ShopWallet.class);

        // 已确认收货的订单扣除已结算的金额
        if (Objects.equals(order.getStatus(), OrderStatus.SUCCESS.value())) {
            // 平台把退款金额归还商家，商家退还金额到用户
            if (shopWallet.getSettledAmount() < shopRealRefundAmount) {
                throw new MaoshiShopBindException("您的可提现金额不足，无法进行退款");
            }
            newShopWallet.setSettledAmount(Arith.sub(shopWallet.getSettledAmount(), shopRealRefundAmount));
            newShopWallet.setTotalSettledAmount(Arith.sub(shopWallet.getTotalSettledAmount(), shopRealRefundAmount));
        }
        // 1. 订单未确认收货则扣未结算金额
        // 2. 商家撤销拼团活动时失效拼团时，将待成团的队伍进行退款
        if (Objects.equals(order.getStatus(), OrderStatus.PADYED.value()) || Objects.equals(order.getStatus(), OrderStatus.CONSIGNMENT.value()) || Objects.equals(order.getStatus(), OrderStatus.WAIT_GROUP.value())) {

            if (shopWallet.getUnsettledAmount() < shopRealRefundAmount) {
                throw new MaoshiShopBindException("您的待结算金额不足，无法进行退款");
            }
            newShopWallet.setUnsettledAmount(Arith.sub(shopWallet.getUnsettledAmount(),shopRealRefundAmount));
        }
        newShopWallet.setUpdateTime(new Date());
        // 添加钱包日志记录
        shopWalletLogService.saveShopWalletLog(shopWallet, newShopWallet, ShopWalletChangeReason.ORDER_REFUND, orderRefundDto.getOrderNumber());

        // 更新商家钱包信息
        if (shopWalletMapper.updateById(newShopWallet) != 1) {
            throw new MaoshiShopBindException("更新店铺钱包信息失败");
        }
    }

    /**
     * 不同意退款
     */
    private void disagreeRefund(OrderRefundParam orderRefundParam, OrderRefundDto orderRefundDto) {
        // 不同意退款
        orderRefundDto.setReturnMoneySts(ReturnMoneyStsType.REJECT.value());
        orderRefundDto.setReturnMoneySts(ReturnMoneyStsType.FAIL.value());
        // 拒绝原因
        orderRefundDto.setRejectMessage(orderRefundParam.getRejectMessage());
        orderRefundDto.setRejectTime( new Date());

        // 更新订单
        Order order = orderService.getById(orderRefundDto.getOrderId());
        order.setRefundStatus(RefundStatusEnum.DISAGREE.value());
        orderService.updateById(order);
    }

    /**
     * 买家提交物流信息
     *
     * @param orderRefund    退款单对象
     * @param refundDelivery 退款物流单对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitExpress(OrderRefund orderRefund, RefundDelivery refundDelivery) {
        refundDeliveryService.updateById(refundDelivery);
        updateById(orderRefund);
    }

    /**
     * 系统自动退款操作（批量）
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void systemAutoRefundBatch(List<OrderRefundDto> orderRefundDtoList) {
        for (OrderRefundDto orderRefundDto : orderRefundDtoList) {
            agreeRefund(orderRefundDto);
            update(orderRefundDto, new LambdaQueryWrapper<OrderRefund>()
                    .eq(OrderRefund::getRefundSn, orderRefundDto.getRefundSn()));
            // 减少商家未结算金额
            subShopSettlementAmount(orderRefundDto);

            try {
                submitWxRefund(orderRefundDto);
            } catch (MaoshiShopBindException e) {
                WxPayException wxPayException = (WxPayException) e.getObject();
                if (Objects.equals("订单已全额退款", wxPayException.getErrCodeDes())) {
                    continue;
                }
                throw e;
            }
        }
    }

    /**
     * 系统验证退款结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void verifyRefund(OrderRefundDto orderRefundDto, String payRefundId) {
        // 查询退款结算记录
        RefundOrderSettlement refundOrderSettlement = refundOrderSettlementService.getOne(
                new LambdaQueryWrapper<RefundOrderSettlement>().eq(RefundOrderSettlement::getRefundSn, orderRefundDto.getRefundSn()));

        // 判断退款结算记录是否为申请状态
        if (refundOrderSettlement == null || refundOrderSettlement.getRefundStatus() != 1) {
            throw new MaoshiShopBindException("退款已处理，无需重复处理");
        }

        // 更新退款结算记录
        refundOrderSettlement.setPayRefundId(payRefundId);
        refundOrderSettlement.setRefundStatus(2);
        if (!refundOrderSettlementService.updateById(refundOrderSettlement)) {
            throw new MaoshiShopBindException("服务器繁忙，请稍后再试");
        }
        // 更新订单
        Order order = orderService.getOrderByOrderNumber(orderRefundDto.getOrderNumber());
        int oldOrderStatus = order.getStatus();


        // 退款成功（保存退款结算记录、更新订单、还原库存）
        refundSuccess(order, orderRefundDto);

        // 推送退款成功事件
        applicationContext.publishEvent(new OrderRefundSuccessEvent(orderRefundDto));

        // 非整单退款 && 以前订单没有结算（确认收货）&& 现在订单关闭了 && 因为退款导致订单关闭
        // 如果订单因为退款关闭，要进行结算给商家的操作，这个结算的计算是退款完成后结算的，所以不要随便改变顺序
        if (!Objects.equals(orderRefundDto.getRefundType(), RefundType.ALL.value())
                && !Objects.equals(oldOrderStatus, OrderStatus.SUCCESS.value())
                    && Objects.equals(order.getStatus(), OrderStatus.CLOSE.value())
                        && Objects.equals(order.getCloseType(), OrderCloseType.REFUND.value())) {
            applicationContext.publishEvent(new ReceiptOrderEvent(order));
        }


    }

    /**
     * 同意退款操作（生成结算记录）
     */
    private void agreeRefund(OrderRefundDto orderRefundDto) {
        Date nowDate = new Date();
        // 查询支付记录
        OrderSettlement orderSettlement = orderSettlementService.getById(orderRefundDto.getSettlementId());
        if (orderSettlement == null) {
            throw new MaoshiShopBindException("没有查询到支付记录，无法申请退款");
        }
        if (refundOrderSettlementService.count(new LambdaQueryWrapper<RefundOrderSettlement>().eq(RefundOrderSettlement::getRefundSn, orderRefundDto.getRefundSn())) > 0) {
            throw new MaoshiShopBindException("订单正在进行退款结算，请勿重复操作");
        }

        // 生成退款结算记录
        RefundOrderSettlement refundOrderSettlement = new RefundOrderSettlement();
        refundOrderSettlement.setOrderNumber(orderRefundDto.getOrderNumber());
        refundOrderSettlement.setOrderPayNo(orderRefundDto.getOrderPayNo());
        refundOrderSettlement.setRefundSn(orderRefundDto.getRefundSn());
        refundOrderSettlement.setPayType(orderSettlement.getPayType());
        refundOrderSettlement.setPayTypeName(orderSettlement.getPayTypeName());
        refundOrderSettlement.setRefundAmount(orderRefundDto.getRefundAmount());
        refundOrderSettlement.setOrderTotalAmount(orderRefundDto.getOrderAmount());
        refundOrderSettlement.setUserId(orderRefundDto.getUserId());
        refundOrderSettlement.setRefundStatus(1);
        refundOrderSettlement.setCreateTime(nowDate);
        refundOrderSettlement.setUpdateTime(nowDate);
        refundOrderSettlementService.save(refundOrderSettlement);

        // 更新退款单信息
        orderRefundDto.setDecisionTime(nowDate);
        orderRefundDto.setUpdateTime(nowDate);
    }

    /**
     * 提交退款请求（向微信发起退款请求）
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void submitWxRefund(OrderRefundDto orderRefundDto) {
        // 判断是否已同意退款
        if (orderRefundDto.getDecisionTime() == null) {
            return;
        }
        // 查询支付记录
        OrderSettlement orderSettlement = orderSettlementService.getById(orderRefundDto.getSettlementId());
        RefundInfoDto refundInfo = new RefundInfoDto();
        refundInfo.setPayType(PayType.instance(orderSettlement.getPayType()));
        refundInfo.setPayNo(orderSettlement.getPayNo());
        refundInfo.setRefundSn(orderRefundDto.getRefundSn());
        refundInfo.setPayAmount(orderSettlement.getPayAmount());
        refundInfo.setRefundAmount(orderRefundDto.getRefundAmount());
        refundInfo.setNotifyUrl("/order/refund/result/"+orderSettlement.getPayType());
        payManagerService.doRefund(refundInfo);
    }

    @Override
    public double sumRefundSuccessAmountByOrderId(Long orderId) {
        return orderRefundMapper.sumRefundSuccessAmountByOrderId(orderId);
    }

    @Override
    public List<OrderRefund> getProcessingOrderRefundByOrderId(Long orderId) {
        return orderRefundMapper.getProcessingOrderRefundByOrderId(orderId);
    }

    /**
     * 退款成功（更新退款结算记录、更新订单、还原库存）
     */
    private void refundSuccess(Order order, OrderRefundDto orderRefundDto) {
        if (Objects.equals(orderRefundDto.getRefundType(), RefundType.ALL.value())) {
            order.setStatus(OrderStatus.CLOSE.value());
            order.setCancelTime(new Date());
            order.setUpdateTime(new Date());
            order.setCloseType(OrderCloseType.REFUND.value());
            order.setRefundStatus(RefundStatusEnum.SUCCEED.value());
        } else {
            // 查看是否已经达到了所有的订单数量，如是则订单关闭
            int totalRefundNum = 0;
            // 已退款单
            List<OrderRefund> orderRefundList = orderRefundMapper.selectList(new LambdaQueryWrapper<OrderRefund>()
                    .eq(OrderRefund::getOrderId, orderRefundDto.getOrderId())
                    .eq(OrderRefund::getReturnMoneySts, ReturnMoneyStsType.SUCCESS.value())
            );
            if (CollectionUtils.isNotEmpty(orderRefundList)) {
                totalRefundNum += orderRefundList.stream().mapToInt(OrderRefund::getGoodsNum).sum();
            }

            // 整个订单完成退款的时候
            if (totalRefundNum >= order.getProductNums()) {
                order.setStatus(OrderStatus.CLOSE.value());
                order.setCancelTime(new Date());
                order.setUpdateTime(new Date());
                order.setCloseType(OrderCloseType.REFUND.value());
                order.setRefundStatus(RefundStatusEnum.SUCCEED.value());
            } else {
                order.setRefundStatus(RefundStatusEnum.PARTIAL_SUCCESS.value());
            }
        }
        orderService.updateById(order);

        // 还原库存
        OrderRefundDto orderRefund = getOrderRefundByRefundSn(orderRefundDto.getRefundSn());
        List<OrderItem> orderItems = orderRefund.getOrderItems();
        for (OrderItem orderItem : orderItems) {
            Product product = productService.getById(orderItem.getProdId());
            Sku productSuk = skuService.getSkuBySkuId(orderItem.getSkuId());

            int totalStocks = product.getTotalStocks();
            int skuStocks = productSuk.getStocks();
            if (Objects.equals(orderRefundDto.getRefundType(), RefundType.ALL.value())) {
                // 全部退款，还原全部数量
                totalStocks += orderItem.getProdCount();
                skuStocks += orderItem.getProdCount();
            } else {
                // 部分退款，还原退款数量
                totalStocks += orderRefund.getGoodsNum();
                skuStocks += orderRefund.getGoodsNum();
            }
            product.setTotalStocks(totalStocks);
            productSuk.setStocks(skuStocks);
            productService.updateById(product);
            skuService.updateById(productSuk);
            skuService.removeSkuCacheBySkuId(productSuk.getSkuId(), product.getProdId());
        }

        // 退款成功
        orderRefundDto.setReturnMoneySts(ReturnMoneyStsType.SUCCESS.value());
        orderRefundDto.setRefundTime(new Date());
        updateById(orderRefundDto);


    }

    /**
     * 取消申请超时的订单，无论该超时订单处于任何状态
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelWhenTimeOut(List<OrderRefund> orderRefundList) {
        Date now = new Date();
        for (OrderRefund orderRefund : orderRefundList) {
            orderRefund.setReturnMoneySts(ReturnMoneyStsType.FAIL.value());
            orderRefund.setSellerMsg("申请退款超时");
            orderRefund.setShipTime(now);
            orderRefund.setUpdateTime(now);

        }
        updateBatchById(orderRefundList);
    }

    @Override
    public boolean checkRefundDate(Order order) {
        // 判断是否超过支持的退款天数
        if (Objects.equals(order.getStatus(), OrderStatus.SUCCESS.value())) {
            long finallyTime = order.getFinallyTime().getTime();
            long currentTime = System.currentTimeMillis();
            int miniTime = Constant.MAX_FINALLY_REFUND_TIME * 24 * 60 * 60 * 1000;
            if (currentTime - finallyTime > miniTime) {
                return false;
            }
        }
        return true;
    }

    @Override
    public double sumRefundSuccessPlatformAmountByOrderId(Long orderId) {
        return orderRefundMapper.sumRefundSuccessPlatformAmountByOrderId(orderId);
    }
}
